/*
 * @(#)XfsimPlotPanel.java        2003/01/01
 *
 * This file is part of Xfuzzy 3.0, a design environment for fuzzy logic
 * based systems.
 *
 * (c) 2000 IMSE-CNM. The authors may be contacted by the email address:
 *                    xfuzzy-team@imse.cnm.es
 *
 * Xfuzzy is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation.
 *
 * Xfuzzy is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 */


package xfuzzy.xfsim.model;

import xfuzzy.lang.*;
import xfuzzy.util.*;
import javax.swing.*;
import java.awt.*;

/**
 * Clase que desarrolla un panel de representaci�n gr�fica de dos
 * variables de la simulaci�n
 * 
 * @author Francisco Jos� Moreno Velo
 *
 */
public class XfsimPlotPanel extends JPanel  {
	
	//----------------------------------------------------------------------------//
	//                            COSTANTES PRIVADAS                              //
	//----------------------------------------------------------------------------//

	/**
	 * C�digo asociado a la clase serializable
	 */
	private static final long serialVersionUID = 95505666603061L;
	
	//----------------------------------------------------------------------------//
	//                            MIEMBROS PRIVADOS                               //
	//----------------------------------------------------------------------------//
			
	/**
	 * Variable a representar en el eje X
	 */
	private int xvar;
	
	/**
	 * Variable a representar en el eje Y
	 */
	private int yvar;
	
	/**
	 * Tipo de representaci�n (con puntos, c�rculos, l�neas, etc.)
	 */
	private int kind;

	/**
	 * Nombre de la variable a representar en el eje X
	 */
	private String xname;
	
	/**
	 * Nombre de la variable a representar en el eje Y
	 */
	private String yname;
	
	/**
	 * Valores l�mite de las variables
	 */
	private double xmin,xmax,ymin,ymax;
	
	/**
	 * Posici�n de los ejes
	 */
	private int x0,x1,y0,ym,y1;
	
	/**
	 * Componente gr�fico
	 */
	private Graphics g;
	
	/**
	 * Descripci�n de la fuente
	 */
	private FontMetrics fm;
	
	/**
	 * Valores de la fuente
	 */
	private int ascent, fmheight;
	
	/**
	 * Valores hist�ricos
	 */
	private double[] xhist, yhist;
	
	/**
	 * Valores previos para la representaci�n con l�neas
	 */
	private int xprev, yprev;
	
	/**
	 * N�mero de reescalados del eje X
	 */
	private int xresc;
	
	/**
	 * �ndice de los datos a almacenar
	 */
	private int index;
	
	//----------------------------------------------------------------------------//
	//                                CONSTRUCTOR                                 //
	//----------------------------------------------------------------------------//
	
	/**
	 * Constructor
	 */
	public XfsimPlotPanel(XfsimPlot config, Specification spec) {	
		initialize(spec,config);
	}
	
	//----------------------------------------------------------------------------//
	//                             M�TODOS P�BLICOS                               //
	//----------------------------------------------------------------------------//
	
	/**
	 * Dibuja un nuevo punto de la representaci�n
	 */
	public void iter(double iter, double time, double[] fzst, double[] ptst) {
		this.index++;
		if(index == xhist.length) {
			double xaux[] = new double[2*xhist.length];
			System.arraycopy(xhist,0,xaux,0,xhist.length);
			this.xhist = xaux;
			double yaux[] = new double[2*yhist.length];
			System.arraycopy(yhist,0,yaux,0,yhist.length);
			this.yhist = yaux;
		}
		if(xvar == 0) xhist[index] = iter;
		else if(xvar == 1) xhist[index] = time/1000;
		else if(xvar<fzst.length+2) xhist[index] = fzst[xvar-2];
		else xhist[index] = ptst[xvar-2-fzst.length];
		if(yvar<fzst.length) yhist[index] = fzst[yvar];
		else yhist[index] = ptst[yvar-fzst.length];
		Rectangle r = paintStatus(index);
		if(r != null) repaint(r);
	}
		
	/**
	 * Pinta la gr�fica completa
	 */
	public void paint(Graphics g) {
		super.paint(g);
		setConstants(g);
		paintAxis();
		paintHist();
	}
			
	//----------------------------------------------------------------------------//
	//                             M�TODOS PRIVADOS                               //
	//----------------------------------------------------------------------------//
	
	/**
	 * Inicializa la representaci�n gr�fica
	 */
	private void initialize(Specification spec, XfsimPlot config) {
		setBackground(XConstants.textbackground);
		this.xhist = new double[100];
		this.yhist = new double[100];
		this.index = -1;
		this.xresc = 0;
		this.kind = config.getKind();
		
		Variable input[] = spec.getSystemModule().getInputs();
		Variable output[] = spec.getSystemModule().getOutputs();
		String cxvar = config.getXvar();
		String cyvar = config.getYvar();
		if(cxvar.equals("n")) {
			this.xvar = 0;
			this.xname = "_n"; 
			this.xmin = 0; 
			this.xmax = 100;
		} else if(cxvar.equals("t")) {
			this.xvar = 1;
			this.xname = "_t"; 
			this.xmin = 0; 
			this.xmax = 10;
		} else if(cxvar.charAt(0) == 'o') {
			int index = Integer.parseInt(cxvar.trim().substring(1));
			this.xvar = index+2;
			this.xname = output[index].getName();
			this.xmin = output[index].point(0.0);
			this.xmax = output[index].point(1.0);
		} else if(cxvar.charAt(0) == 'i') {
			int index = Integer.parseInt(cxvar.trim().substring(1));
			this.xvar = index+output.length+2;
			this.xname = input[index].getName();
			this.xmin = input[index].point(0.0);
			this.xmax = input[index].point(1.0);
		}
		
		if(cyvar.charAt(0) == 'o') {
			int index = Integer.parseInt(cyvar.trim().substring(1));
			this.yvar = index;
			this.yname = output[index].getName();
			this.ymin = output[index].point(0.0);
			this.ymax = output[index].point(1.0);
		} else if(cyvar.charAt(0) == 'i') {
			int index = Integer.parseInt(cyvar.trim().substring(1));
			this.yvar = index+output.length;
			this.yname = input[index].getName();
			this.ymin = input[index].point(0.0);
			this.ymax = input[index].point(1.0);
		}		
	}
	
	/**
	 * Inicializa algunas constantes internas
	 */
	private void setConstants(Graphics g) {
		this.g = g;
		this.fm = getFontMetrics(getFont());
		this.ascent = fm.getAscent();
		this.fmheight = fm.getHeight();
		
		Dimension size = getSize();
		this.x0 = 100;
		this.x1 = size.width - 100;
		this.y0 = size.height - 50;
		this.y1 = 30;
		this.ym = (y0+y1)/2;
	}
	
	/**
	 * Pinta los ejes de la representaci�n
	 */
	private void paintAxis() {
		g.drawRect(x0,y1,x1-x0,y0-y1);
		g.drawLine(x0,y1,x0-5,y1);
		g.drawLine(x0,ym,x0-5,ym);
		g.drawLine(x0,y0,x0-5,y0);
		g.drawLine(x1,y1,x1+5,y1);
		g.drawLine(x1,ym,x1+5,ym);
		g.drawLine(x1,y0,x1+5,y0);
		int xdiv = (xvar>1? 2 : 5);
		for(int i=0; i<=xdiv; i++) {
			int lpos = x0+(x1-x0)*i/xdiv;
			g.drawLine(lpos,y0,lpos,y0+5);
		}
		
		String xtitle = xname+(xvar == 1? " (s)": "");
		int yposx = (x0-fm.stringWidth(xtitle))/2;
		int yposy = (y1-fmheight)/2+ascent;
		int xposx = x1+(getSize().width-x1-fm.stringWidth(yname))/2;
		int xposy = y0+(getSize().height-y0)/2+ascent;
		String stymin = ""+ymin;
		String stymax = ""+ymax;
		String stymean = ""+((ymax+ymin)/2);
		g.drawString(xtitle,xposx,xposy);
		g.drawString(yname,yposx,yposy);
		g.drawString(stymax,x0-10-fm.stringWidth(stymax),y1+ascent/2);
		g.drawString(stymean,x0-10-fm.stringWidth(stymean),ym+ascent/2);
		g.drawString(stymin,x0-10-fm.stringWidth(stymin),y0+ascent/2);
		g.drawString(stymax,x1+10,y1+ascent/2);
		g.drawString(stymean,x1+10,ym+ascent/2);
		g.drawString(stymin,x1+10,y0+ascent/2);
		for(int i=0; i<=xdiv; i++) {
			String stval = ""+(xmin+(xmax-xmin)*i/xdiv);
			int stpos = x0+(x1-x0)*i/xdiv-fm.stringWidth(stval)/2;
			g.drawString(stval,stpos,y0+10+ascent);
		}
	}
	
	/**
	 * Pinta un punto de la representaci�n
	 */
	private Rectangle paintStatus(int index) {
		double xvalue = xhist[index];
		double yvalue = yhist[index];
		if(xvalue > xmax && xvar <= 1) {
			xresc++;
			xmax = (xvar ==0? 100 : 10);
			for(int i=0; i<xresc ; i++) if(i%3 != 0) xmax*=2; else xmax=xmax*5/2;
			repaint();
			return null;
		}
		
		int xpos = x0 + (int) ((x1-x0)*(xvalue-xmin)/(xmax-xmin));
		int ypos = y0 + (int) ((y1-y0)*(yvalue-ymin)/(ymax-ymin));
		switch(kind) {
		case 0:
			if(index != 0) g.drawLine(xprev,yprev,xpos,ypos);
			Rectangle r = new Rectangle(xprev,yprev,xpos-xprev,ypos-yprev);
			xprev = xpos;
			yprev = ypos;
			return r;
		case 1:
			g.fillOval(xpos-2,ypos-2,4,4);
			return new Rectangle(xpos-2,ypos-2,4,4);
		case 2:
			g.drawRect(xpos-3,ypos-3,6,6);
			return new Rectangle(xpos-3,ypos-3,6,6);
		case 3:
			g.drawOval(xpos-3,ypos-3,6,6);
			return new Rectangle(xpos-3,ypos-3,6,6);
		case 4:
			g.drawOval(xpos-5,ypos-5,10,10);
			return new Rectangle(xpos-5,ypos-5,10,10);
		case 5:
			g.drawOval(xpos-10,ypos-10,20,20);
			return new Rectangle(xpos-10,ypos-10,20,20);
		default: return null;
		}
	}
	
	/**
	 * Pinta todos los puntos de la representaci�n
	 */
	private void paintHist() {
		for(int i=0; i<index; i++) paintStatus(i);
	}
}
